package lk.icta.core.dao.impl;

import lk.icta.common.dao.impl.BaseDAO;
import lk.icta.core.dao.AlienationDAO;
import lk.icta.core.entity.Alienation;
import lk.icta.core.entity.AlienationMethod;
import lk.icta.core.entity.LandAssociation;
import lk.icta.user.entity.User;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.Query;
import java.util.Date;
import java.util.List;

/**
 * @author Duminda Dharmakeerthi
 */
public class AlienationDAOImpl extends BaseDAO implements AlienationDAO {

    @Transactional(propagation = Propagation.MANDATORY)
    public void addAlienation(Alienation alienation, User user) {
        logger.debug("Add an Alienation for the land {} by {}", alienation.getLand().getNameOfTheLand(), user.getUsername());
        alienation.getLifeCycleInfo().setCreatedUser(user);
        alienation.getLifeCycleInfo().setCreatedTimeStamp(new Date());
        alienation.getLifeCycleInfo().setLastUpdatedUser(user);
        alienation.getLifeCycleInfo().setLastUpdatedTimeStamp(new Date());
        em.persist(alienation);
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void updateAlienation(Alienation alienation, User user) {
        logger.debug("Update an Alienation for the land {} by {}", alienation.getLand().getNameOfTheLand(), user.getUsername());
        alienation.getLifeCycleInfo().setLastUpdatedUser(user);
        alienation.getLifeCycleInfo().setLastUpdatedTimeStamp(new Date());
        em.merge(alienation);
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void deleteAlienation(Alienation alienation, User user) {
        logger.debug("Delete alienation {} by {}", alienation.getId(), user.getUsername());
        alienation.getLifeCycleInfo().setLastUpdatedUser(user);
        alienation.getLifeCycleInfo().setLastUpdatedTimeStamp(new Date());
        alienation.setStatus(Alienation.State.ARCHIVED);
        em.merge(alienation);
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void deleteAlienationFromDatabase(long alienationId, User user) {
        logger.debug("Delete alienation {} by {}", alienationId, user.getUsername());
        Query q = em.createNamedQuery("deleteAlienationFromDatabase");
        q.setParameter("alienationId", alienationId);
        q.executeUpdate();
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void removeAlienation(long id, User user) {
        logger.debug("remove alienation {} by {}", id, user.getUsername());
        em.remove(em.find(Alienation.class, id));
    }

    @Transactional(propagation = Propagation.MANDATORY)
    public void addLandAlienationMetadata(LandAssociation LAMMetadata, User user) {
        logger.debug("Adding land Alienation Metadata by {}", user.getUsername());
        em.persist(LAMMetadata);
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Alienation getAlienationById(Long id, User user) {
        logger.debug("Loading Alienation [Id: {}]", id);
        return em.find(Alienation.class, id);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Alienation> getAlienationByLand(Long landId, User user) {
        logger.debug("Loading alienation history of the land [Land id: {}]", landId);
        Query q = em.createNamedQuery("getAlienationByLand");
        q.setParameter("landId", landId);
        return q.getResultList();
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Alienation> getAlienationByHolder(Long holderId, User user) {
        logger.debug("Loading alienation records of a person [Person id: {}]", holderId);
        Query q = em.createNamedQuery("getAlienationByHolder");
        q.setParameter("personId", holderId);
        return q.getResultList();
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Alienation> getAllAlienation(User user) {
        logger.debug("List Alienation records");
        //Query q = em.createNamedQuery("getAllAlienation");
        Query q = em.createNamedQuery("getAllAlienation").setFirstResult(10);
        return q.getResultList();
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Alienation> getAllActiveAlienation(User user) {
        logger.debug("List Alienation records");
        Query q = em.createNamedQuery("getAllActiveAlienation");
        q.setParameter("dsDivisionId", user.getDsDivisions().iterator().next().getId());
        return q.getResultList();
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Alienation getActiveAlienationByLandId(long landId, User user) {
        logger.debug("Find the active alienation of land {}", landId);
        Query q = em.createNamedQuery("getActiveAlienationByLandId");
        q.setParameter("landId", landId);
        List res = q.getResultList();
        return (res.size() > 0) ? (Alienation) q.getSingleResult() : null;
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public Alienation getPendingAlienationByLandId(long landId, User user) {
        logger.debug("Find the pending alienation of land {}", landId);
        Query q = em.createNamedQuery("getPendingAlienationByLandId");
        q.setParameter("landId", landId);
        List res = q.getResultList();
        return (res.size() > 0) ? (Alienation) q.getSingleResult() : null;
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Alienation> getGrantAlienationHistory(long grantId, User user) {
        logger.debug("List Alienation History of Grant {}", grantId);
        Query q = em.createNamedQuery("getGrantAlienationHistory");
        q.setParameter("grantId", grantId);
        return q.getResultList();
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Alienation> getActiveAlienationByGNDivision(int gnDivisionId, User user) {
        logger.debug("Listing Alienation of G.N. Division {}", gnDivisionId);
        Query q = em.createNamedQuery("getActiveAlienationByGNDivision");
        q.setParameter("id", gnDivisionId);
        return q.getResultList();
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public AlienationMethod getAlienationMethodById(int id, User user) {
        logger.debug("Find Alienation Method By Id : ", user.getUsername());
        Query q = em.createNamedQuery("getAlienationMethodById");
        q.setParameter("id", id);
        List result = q.getResultList();
        if (!result.isEmpty()) {
            return (AlienationMethod) result.get(0);
        } else {
            return null;
        }
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Alienation> getChildAlienationNodesByParentId(long parentId, User user) {
        logger.debug("Listing Child Alienation of {} by {}", parentId, user.getUsername());
        Query q = em.createNamedQuery("getChildAlienationNodesByParentId");
        q.setParameter("parentId", parentId);
        return q.getResultList();
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Alienation> getAlienationByAlienationMethod(int alienationMethod, User user) {
        logger.debug("Listing alienation by alienation method {} by {}", alienationMethod, user.getUsername());
        Query q = em.createNamedQuery("getAlienationByAlienationMethod");
        q.setParameter("alienationMethod", alienationMethod);
        q.setParameter("dsDivisionId", user.getDsDivisions().iterator().next().getId());
        return q.getResultList();
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Alienation> getAllAlienationByAlienationMethod(int alienationMethod, User user) {
        logger.debug("Listing all alienation by alienation method {} by {}", alienationMethod, user.getUsername());
        Query q = em.createNamedQuery("getAllAlienationByAlienationMethod");
        q.setParameter("alienationMethod", alienationMethod);

        logger.debug("Listing all alienation by alienation method {} size {}", alienationMethod, q.getResultList().size());
        return q.getResultList();
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public Alienation getActiveAlienationByReferenceNo(String referenceNo) {
        logger.debug("Finding alienation by ReferenceNo{} by {}", referenceNo);
        Query q = em.createNamedQuery("getAlienationByReferenceNo");
        q.setParameter("referenceNo", referenceNo);
        List result = q.getResultList();
        if (!result.isEmpty()) {
            return (Alienation) result.get(0);
        } else {
            return null;
        }
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Alienation getActiveApprovedAlienationByReferenceNo(String referenceNo) {
        logger.debug("Finding approved alienation by ReferenceNo{} by {}", referenceNo);
        Query q = em.createNamedQuery("getApprovedAlienationByReferenceNo");
        q.setParameter("referenceNo", referenceNo);
        List result = q.getResultList();
        if (!result.isEmpty()) {
            return (Alienation) result.get(0);
        } else {
            return null;
        }
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Boolean isExistsReference(String referenceNo) {
        logger.debug("Check alienation with reference {} ", referenceNo);
        Query q = em.createNamedQuery("getAlienationByReferenceNo");
        q.setParameter("referenceNo", referenceNo);
        return !q.getResultList().isEmpty();
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Alienation> getPendingAlienationByAlienationMethod(int alienationId, User user) {
        logger.debug("Get pending alienation list with id {} ", alienationId);
        Query q = em.createNamedQuery("getAlienationFromAlienationMethodAndPendingState");
        populateDsDivisionList(q, user);
        q.setParameter("alienationId", alienationId);
        return q.getResultList();
    }

    @Override
    public List<Alienation> searchPermit(User user) {
        logger.debug("Finding approved Land Permits to land grant by user {}", user);
        Query q = em.createNamedQuery("searchApprovedLandPermits");
        q.setParameter("dsDivisionId", user.getDsDivisions().iterator().next().getId());
        List result = q.getResultList();
        if (!result.isEmpty()) {
            logger.debug("if-----------------------------------");
            return result;
        } else {
            logger.debug("else-----------------------------------");
            return null;
        }
    }


    private void populateDsDivisionList(Query q, User user) {
        if (user.getDsDivisions() != null && user.getDsDivisions().size() != 0) {
            //for orlp and lcg, find all apps, ignore DS filter
            logger.debug("ds div list size : {}", user.getDsDivisions().size());
            q.setParameter("ignoreDS", 0);
            q.setParameter("DSList", user.getDsDivisions());

        } else {
            q.setParameter("ignoreDS", 1);
            logger.debug("ds div list : {} ignoreDS :{}", user.getDsDivisions(), 1);
            q.setParameter("DSList", null);
        }

    }
}
